package app

import (
	"context"
	"k8s.io/client-go/discovery"
	memory "k8s.io/client-go/discovery/cached"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/dynamic/dynamicinformer"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/restmapper"
	"k8s.io/client-go/tools/clientcmd"
	"operator/pkg/controller"
	"operator/pkg/format"
	tcp2 "operator/pkg/network/meshnet/tcp"
	"operator/pkg/util"
	"operator/pkg/webhook"
	"os"
	"os/signal"
	"syscall"
	"time"
)

const defaultPath = "D:\\gopath\\src\\operator\\config/kubeconfig"

type MeshServer struct {
	client           dynamic.Interface
	meshController   *controller.MeshController
	podController    *controller.PodController
	egressController *controller.EgressController
	dSharedInformer  dynamicinformer.DynamicSharedInformerFactory
	hook             *webhook.KubernetesWebhook
	updateCh         chan []byte
	egressCh         chan []byte
	tcpServer        *tcp2.NewTcpServer
}

func (server *MeshServer) Run() {
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGKILL, syscall.SIGINT)

	stopCh := make(chan struct{})
	go server.hook.Run()
	go server.tcpServer.Run(stopCh)
	go server.updateMesh(stopCh)
	server.dSharedInformer.Start(stopCh)
	server.meshController.Run(stopCh)
	server.podController.Run(stopCh)
	server.egressController.Run(stopCh)

	format.Println("cmd/server/app/server.go", 51, "app run")

	select {
	case x := <-ch:
		switch x {
		case syscall.SIGINT:
			format.Println("cmd/server/app/server.go", 56, "app stop")
			server.meshController.RemoveAll()
		}
	}
}

func (s *MeshServer) list(data []byte) []byte {
	return s.podController.List()
}

func (s *MeshServer) updateMesh(stopCh chan struct{}) {
	for {
		select {
		case data := <-s.updateCh:
			result := s.list(data)
			s.tcpServer.BroadCast("update", result)
		case data := <-s.egressCh:
			s.tcpServer.BroadCast("update_egress", data)
		case <-stopCh:
			break
		}
	}
}

func NewMeshServer() *MeshServer {
	c, err := rest.InClusterConfig()
	if err != nil {
		c, err = clientcmd.BuildConfigFromFlags("", defaultPath)
		if err != nil {
			panic(err)
		}
	}

	client := dynamic.NewForConfigOrDie(c)

	dSharedInformer := dynamicinformer.NewDynamicSharedInformerFactory(client, time.Minute)
	if dSharedInformer == nil {
		os.Exit(10)
	}

	updateCh := make(chan []byte)
	addIpCh := make(chan string)
	egressCh := make(chan []byte)

	dc, err := discovery.NewDiscoveryClientForConfig(c)
	if err != nil {
		format.Errors("cmd/server/app/server.go", 103, err.Error())
		os.Exit(1)
	}
	mapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(dc))
	ctx := context.Background()

	meshcontrol := controller.NewMeshController(ctx, c, dSharedInformer, mapper, client, 2, 5)
	podcontrol := controller.NewPodController(ctx, c, dSharedInformer, mapper, client, 2, 5, updateCh, addIpCh)
	egresscontrol := controller.NewEgressController(ctx, dSharedInformer, mapper, client, 2, 5, egressCh)

	tcp, err := tcp2.NewServer(util.TcpServerPort, addIpCh)
	if err != nil {
		os.Exit(2)
	}

	ip, err := podcontrol.GetMySelfIp()
	if err != nil {
		format.Errors("cmd/server/app/server.go", 120, err.Error())
		os.Exit(3)
	}

	tip, err := podcontrol.GetTraceIp()
	if err != nil {
		format.Errors("cmd/server/app/server.go", 126, err.Error())
		os.Exit(3)
	}

	ms := &MeshServer{
		client:           client,
		meshController:   meshcontrol,
		podController:    podcontrol,
		dSharedInformer:  dSharedInformer,
		egressController: egresscontrol,
		hook:             webhook.NewDefaultWebHook(ip, tip),
		tcpServer:        tcp,
		updateCh:         updateCh,
		egressCh:         egressCh,
	}

	ms.tcpServer.AddFn("list", ms.list)
	ms.tcpServer.AddFn("getTraceIp", func(b []byte) []byte {
		return []byte(tip)
	})

	return ms
}
